package com.pocket.demo.core.security.filters;

import cn.hutool.json.JSONUtil;
import com.pocket.demo.base.enums.MsgCodeEnum;
import com.pocket.demo.base.utils.ServletUtils;
import com.pocket.demo.business.service.PocketUserDetailsService;
import com.pocket.demo.core.context.TokenContext;
import com.pocket.demo.core.i18n.MessageSourceService;
import com.pocket.demo.core.security.PocketUser;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/**
 * 【demo-SpringSecurity-4.3】
 * Filter 和 OncePerRequestFilter 区别：
 *      Filter：请求 servlet 时会被 Filter 拦截，当请求转发给另一个 servlet时，会被相同的 Filter 拦截再次拦截。
 *      OncePerRequestFilter：一个请求只被过滤器拦截一次。请求转发不会第二次触发过滤器。
 *
 * @author zhaozhile
 */
public class TokenAuthenticationTokenFilter extends OncePerRequestFilter implements Ordered {

    private static final Logger LOGGER = LoggerFactory.getLogger(TokenAuthenticationTokenFilter.class);

    private PocketUserDetailsService userDetailsService;
    public TokenAuthenticationTokenFilter(PocketUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = TokenContext.getToken();
        if (StringUtils.isBlank(token)) {
            LOGGER.debug("request【{}】token is null, skip", request.getRequestURI());
            filterChain.doFilter(request, response);
            return;
        }
        PocketUser cacheUser =  userDetailsService.loadUserBySessionId(token);
        if (cacheUser == null){
            ServletUtils.renderString(response, JSONUtil.toJsonStr(MessageSourceService.getRestResult( MsgCodeEnum.ERROR_ACCESS_TOKEN_EXPIRED, new Object[0])));
        }

        /**
         * 此构造方法中会调用 super.setAuthenticated(true)，表示已认证。
         *      第一个参数：用户信息
         *      第二个参数：令牌
         *      第三个参数：Collection集合，有关权限的信息，【demo-SpringSecurity-5.3】【demo-SpringSecurity-6.2】
         */
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(cacheUser, token, cacheUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        filterChain.doFilter(request, response);
    }

    @Override
    public int getOrder() {
        return 2;
    }
}
